繞了一大圈終於要開始寫物件導向的核心概念了。
首先,第一個要分享的是封裝 (Encapsulation)
,封裝的概念是把資料包在物件之中,讓使用者只能採用公開的特定方法才可以取得可查詢的資料,其他類別內部的方式與資料都是無法直接取用的。這是一種資料保護的方式,可避免資料被任意的更改,也強化的物件之間的獨立。
以車輛舉例來說,如果車子是一個物件,無法從外面看到他目前的油量、里程數等等資訊,這些就是被封裝在車子這個物件中的資料。必須拿正確的感應器以「發動」這個方法發動車輛後,才會去執行顯示電子儀表板,讓人查看到車輛的資訊。
class 車輛 {
// 油量, 里程數...都是封裝在車輛物件的屬性
private int 油量;
private int 里程數;
// 啟動程序, 顯示儀表板...都是封裝在車輛物件的方法,外部無法訪問
private void 啟動程序{
...
}
private void 顯示儀表板{
show(油量, 里程數)
...
}
// ** 開放 ** 給使用者訪問的方法
public void 發動(int 感應器ID){
if (感應器 == XXXX){
啟動程序();
顯示儀表板();
}
}
}
如果沒有被做好封裝的物件,就會有機率因為外部的訪問,而產生錯誤的結果。
舉例來說,如果有一台車的物件,內部含有廠牌, 售價的屬性:
// 定義 Car 類別 但是開放屬性可供訪問
class Car{
public String brand = "BMW";
public int price = 25000;
...
public void sell(){
System.out.println("This car sells for " + price + " dollars.");
}
}
// 創建一個物件 car
Car car = new Car();
// 因為是 public 屬性可以被直接訪問
System.out.println(car.price);
// Out: 25000
car.sell();
// Out: This car sells for 25000 dollars.
// 但也可以直接被外部修改
car.price = -1000;
car.sell();
// Out: This car sells for -1000 dollars.
從上面的例子發現,我們都知道一台車售價是 -1,000 鎂是完全不合理的,但如果開放屬性被訪問的話,就很難避免外部直接對屬性或方法進行修改,產生意料之外的結果。
因此,為達成 Encapsulation
的目標,在定義類別時,我們通常會將屬性與不想被訪問的內部方法盡可能的設定成 private
,若是要從外部更改獲釋存取,則會建立方法 setter 和 getter 或其他公開方法,來管控存取屬性的方法。所以把剛剛的範例改寫成這樣。
class Car{
// 將屬性設置為 private
private String brand = "BMW";
private int price = 25000;
public void sell(){
System.out.println("This car sells for " + price + " dollars.");
}
// 增加 setter 和 getter
public int getPrice() {
return price;
}
public void setPrice(int price) {
// 限制 price 必須大於兩萬,否則會發出提醒
if (price >= 20000) {
this.price = price;
} else {
System.out.println("Error: price need to over 20,000 dollars");
}
}
---
// 創建一個物件 car
Car car = new Car();
car.price // 無法被訪問
// 要使用 getter 才能取得 price
car.getPrice()
// 使用 setter 設定 price 值
car.setPrice(-1000);
// Error: price need to over 20,000 dollars
// 可以排除不符合規則的輸入
透過 private 的設定,還有 getter & setter 等公開方法的管控,可增加程式的穩定度,達到封裝Encapsulation
的效果。